#include "GameWorld.h"
#include <algorithm>

GameWorld::GameWorld() : camera_(nullptr) {}

GameWorld::~GameWorld() {
    clear();
}

GameObject* GameWorld::createGameObject(const std::string& name) {
    auto gameObject = std::make_unique<GameObject>(name);
    GameObject* ptr = gameObject.get();
    gameObjects_.push_back(std::move(gameObject));
    return ptr;
}

void GameWorld::destroyGameObject(GameObject* gameObject) {
    if (!gameObject) return;
    
    auto it = std::find_if(gameObjects_.begin(), gameObjects_.end(),
        [gameObject](const std::unique_ptr<GameObject>& obj) {
            return obj.get() == gameObject;
        });
    
    if (it != gameObjects_.end()) {
        toDestroy_.push_back(std::move(*it));
        gameObjects_.erase(it);
    }
}

void GameWorld::destroyGameObject(const std::string& name) {
    GameObject* obj = findGameObject(name);
    if (obj) {
        destroyGameObject(obj);
    }
}

GameObject* GameWorld::findGameObject(const std::string& name) {
    auto it = std::find_if(gameObjects_.begin(), gameObjects_.end(),
        [&name](const std::unique_ptr<GameObject>& obj) {
            return obj->getName() == name;
        });
    
    return (it != gameObjects_.end()) ? it->get() : nullptr;
}

std::vector<GameObject*> GameWorld::findGameObjectsWithTag(const std::string& tag) {
    std::vector<GameObject*> result;
    for (const auto& obj : gameObjects_) {
        if (obj->getName().find(tag) != std::string::npos) {
            result.push_back(obj.get());
        }
    }
    return result;
}

void GameWorld::update(float deltaTime) {
    processDestroyQueue();
    
    for (auto& gameObject : gameObjects_) {
        if (gameObject->isActive()) {
            gameObject->update(deltaTime);
        }
    }
}

void GameWorld::render() {
    render(camera_);
}

void GameWorld::render(Camera* camera) {
    // Set the rendering camera for all renderer components
    if (camera) {
        Renderer::setRenderingCamera(camera);
    }
    
    for (auto& gameObject : gameObjects_) {
        if (gameObject->isActive()) {
            // Only render objects that are visible on screen if camera is provided
            if (camera) {
                Transform* transform = gameObject->getTransform();
                if (transform && !camera->isRectVisible(
                    transform->getX(), transform->getY(),
                    transform->getWidth(), transform->getHeight())) {
                    continue; // Skip rendering if not visible
                }
            }
            gameObject->render();
        }
    }
    
    // Clear the rendering camera after rendering
    if (camera) {
        Renderer::setRenderingCamera(nullptr);
    }
}

std::vector<GameObject*> GameWorld::checkCollisions(GameObject* gameObject) {
    std::vector<GameObject*> collisions;
    
    if (!gameObject || !gameObject->getCollisionBox()) {
        return collisions;
    }
    
    for (auto& other : gameObjects_) {
        if (other.get() != gameObject && other->isActive() && other->getCollisionBox()) {
            if (gameObject->checkCollision(*other)) {
                collisions.push_back(other.get());
            }
        }
    }
    
    return collisions;
}

void GameWorld::clear() {
    gameObjects_.clear();
    toDestroy_.clear();
}

const std::vector<std::unique_ptr<GameObject>>& GameWorld::getAllGameObjects() const {
    return gameObjects_;
}

void GameWorld::processDestroyQueue() {
    toDestroy_.clear();
}
